package main

import (
	"fmt"
	"math"
	"time"
)

// UpdateScenario 更新场景类型
type UpdateScenario int

const (
	ScenarioNone                  UpdateScenario = iota // 无偏差
	ScenarioOccasionalFluctuation                       // 偶发波动
	ScenarioTrendChange                                 // 趋势变化
	ScenarioAbnormalEvent                               // 异常事件
	ScenarioPeriodicCalibration                         // 定期校准
)

func (s UpdateScenario) String() string {
	switch s {
	case ScenarioNone:
		return "无偏差"
	case ScenarioOccasionalFluctuation:
		return "偶发波动"
	case ScenarioTrendChange:
		return "趋势变化"
	case ScenarioAbnormalEvent:
		return "异常事件"
	case ScenarioPeriodicCalibration:
		return "定期校准"
	default:
		return "未知场景"
	}
}

// DeviationRecord 偏差记录
type DeviationRecord struct {
	Timestamp      time.Time // 记录时间
	ActualUsage    float64   // 实际用水量
	PredictedUsage float64   // 预测用水量
	Deviation      float64   // 偏差百分比（正数表示实际>预测）
	BlockIndex     int       // 所属3小时块索引
}

// UpdateDecision 更新决策
type UpdateDecision struct {
	Timestamp time.Time      // 决策时间
	Scenario  UpdateScenario // 场景类型

	// 30分钟曲线更新决策
	ShouldUpdate30min bool   // 是否更新30min曲线
	UpdateRange30min  string // 更新范围："current_3h", "6h", "全天"
	UpdateMethod      string // 更新方式："权重衰减", "全部更新"

	// 3小时曲线更新决策
	ShouldReaggregate3h bool // 是否重新聚合3h曲线

	// 补水计划更新决策
	ShouldReplan bool   // 是否重新规划全天补水计划
	Reason       string // 决策原因
	Details      string // 详细信息
}

// DeviationAnalyzer 偏差分析器
type DeviationAnalyzer struct {
	history           []DeviationRecord // 偏差历史记录
	maxHistorySize    int               // 最大历史记录数
	lastCalibration   time.Time         // 上次校准时间
	calibrationPeriod time.Duration     // 校准周期（默认3小时）
}

// NewDeviationAnalyzer 创建偏差分析器
func NewDeviationAnalyzer() *DeviationAnalyzer {
	return &DeviationAnalyzer{
		history:           make([]DeviationRecord, 0),
		maxHistorySize:    12, // 保留最近12条记录（6小时）
		calibrationPeriod: 3 * time.Hour,
		lastCalibration:   time.Now(),
	}
}

// Analyze 分析偏差并返回更新决策
func (da *DeviationAnalyzer) Analyze(actualUsage, predictedUsage float64, currentTime time.Time, blockIndex int) *UpdateDecision {
	// 计算偏差百分比
	deviation := calculateDeviationPercent(actualUsage, predictedUsage)

	// 记录偏差
	record := DeviationRecord{
		Timestamp:      currentTime,
		ActualUsage:    actualUsage,
		PredictedUsage: predictedUsage,
		Deviation:      deviation,
		BlockIndex:     blockIndex,
	}
	da.recordDeviation(record)

	// 判断场景
	scenario := da.classifyScenario(currentTime)

	// 生成决策
	decision := da.makeDecision(scenario, deviation, currentTime)

	return decision
}

// calculateDeviationPercent 计算偏差百分比
func calculateDeviationPercent(actual, predicted float64) float64 {
	if predicted == 0 {
		if actual == 0 {
			return 0
		}
		return 100.0 // 预测为0但实际有值，视为100%偏差
	}
	return round2(((actual - predicted) / predicted) * 100.0)
}

// recordDeviation 记录偏差
func (da *DeviationAnalyzer) recordDeviation(record DeviationRecord) {
	da.history = append(da.history, record)

	// 限制历史记录大小
	if len(da.history) > da.maxHistorySize {
		da.history = da.history[len(da.history)-da.maxHistorySize:]
	}
}

// classifyScenario 场景分类
func (da *DeviationAnalyzer) classifyScenario(currentTime time.Time) UpdateScenario {
	if len(da.history) == 0 {
		return ScenarioNone
	}

	latestRecord := da.history[len(da.history)-1]
	latestDeviation := math.Abs(latestRecord.Deviation)

	// 检查是否需要定期校准
	if currentTime.Sub(da.lastCalibration) >= da.calibrationPeriod {
		return ScenarioPeriodicCalibration
	}

	// 1. 异常事件判断（优先级最高）
	// 规则：单次偏差>30% 或 连续2次偏差>25%
	if latestDeviation > 30.0 {
		return ScenarioAbnormalEvent
	}

	if len(da.history) >= 2 {
		prevRecord := da.history[len(da.history)-2]
		prevDeviation := math.Abs(prevRecord.Deviation)
		if latestDeviation > 25.0 && prevDeviation > 25.0 {
			return ScenarioAbnormalEvent
		}
	}

	// 2. 趋势变化判断
	// 规则：连续2次以上偏差>10%，且必须同向
	if da.detectTrendChange() {
		return ScenarioTrendChange
	}

	// 3. 偶发波动判断
	// 规则：单次偏差10-30%，非连续
	if latestDeviation > 10.0 && latestDeviation <= 30.0 {
		// 检查是否非连续
		if !da.isConsecutiveHighDeviation() {
			return ScenarioOccasionalFluctuation
		}
	}

	// 4. 无偏差
	// 规则：偏差≤10%
	if latestDeviation <= 10.0 {
		return ScenarioNone
	}

	return ScenarioNone
}

// detectTrendChange 检测趋势变化
// 规则：连续2次以上偏差>10%，且必须同向
func (da *DeviationAnalyzer) detectTrendChange() bool {
	if len(da.history) < 2 {
		return false
	}

	// 检查最近的记录
	consecutiveCount := 0
	var lastSign bool // true=正偏差, false=负偏差
	firstIteration := true

	// 从最新往前检查
	for i := len(da.history) - 1; i >= 0; i-- {
		record := da.history[i]
		absDeviation := math.Abs(record.Deviation)

		// 偏差必须>10%
		if absDeviation <= 10.0 {
			break
		}

		currentSign := record.Deviation > 0

		if firstIteration {
			lastSign = currentSign
			consecutiveCount = 1
			firstIteration = false
		} else {
			// 必须同向
			if currentSign == lastSign {
				consecutiveCount++
			} else {
				break
			}
		}
	}

	// 至少连续2次
	return consecutiveCount >= 2
}

// isConsecutiveHighDeviation 检查是否连续高偏差（同向）
func (da *DeviationAnalyzer) isConsecutiveHighDeviation() bool {
	if len(da.history) < 2 {
		return false
	}

	// 检查最近两次
	latest := da.history[len(da.history)-1]
	prev := da.history[len(da.history)-2]

	latestDeviation := math.Abs(latest.Deviation)
	prevDeviation := math.Abs(prev.Deviation)

	// 必须都>10%
	if latestDeviation <= 10.0 || prevDeviation <= 10.0 {
		return false
	}

	// 检查方向是否相同（同向才算连续高偏差）
	latestSign := latest.Deviation > 0
	prevSign := prev.Deviation > 0

	return latestSign == prevSign
}

// makeDecision 生成更新决策
func (da *DeviationAnalyzer) makeDecision(scenario UpdateScenario, deviation float64, currentTime time.Time) *UpdateDecision {
	decision := &UpdateDecision{
		Timestamp: currentTime,
		Scenario:  scenario,
	}

	absDeviation := math.Abs(deviation)

	switch scenario {
	case ScenarioNone:
		// 无偏差：不更新
		decision.ShouldUpdate30min = false
		decision.ShouldReaggregate3h = false
		decision.ShouldReplan = false
		decision.Reason = fmt.Sprintf("偏差%.1f%% ≤ 10%%，在正常范围内", absDeviation)

	case ScenarioOccasionalFluctuation:
		// 偶发波动：更新30min（当前3h），重新聚合3h，不重新规划
		decision.ShouldUpdate30min = true
		decision.UpdateRange30min = "current_3h"
		decision.UpdateMethod = "全部更新" // 默认使用权重衰减，也可以配置
		decision.ShouldReaggregate3h = true
		decision.ShouldReplan = false
		decision.Reason = fmt.Sprintf("单次偏差%.1f%%，判定为偶发波动", absDeviation)
		decision.Details = "仅更新当前3小时块的30分钟曲线，使用全部更新方式"

	case ScenarioTrendChange:
		// 趋势变化：更新30min（全天），重新聚合3h，重新规划
		decision.ShouldUpdate30min = true
		decision.UpdateRange30min = "全天"
		decision.UpdateMethod = "全部更新"
		decision.ShouldReaggregate3h = true
		decision.ShouldReplan = true
		decision.Reason = fmt.Sprintf("检测到趋势变化（连续同向偏差>10%%）")
		decision.Details = fmt.Sprintf("连续%d次记录显示同向偏差，需要重新规划全天补水计划", da.getConsecutiveTrendCount())

	case ScenarioAbnormalEvent:
		// 异常事件：根据偏差程度决定更新范围
		decision.ShouldUpdate30min = true
		decision.ShouldReaggregate3h = true
		decision.ShouldReplan = true
		decision.UpdateMethod = "全部更新"

		if absDeviation > 30.0 {
			decision.UpdateRange30min = "6h"
			decision.Reason = fmt.Sprintf("异常事件：单次偏差%.1f%% > 30%%", absDeviation)
			decision.Details = "更新未来6小时的30分钟曲线，并重新规划补水计划"
		} else {
			// 连续2次偏差>25%
			decision.UpdateRange30min = "全天"
			decision.Reason = "异常事件：连续2次偏差 > 25%"
			decision.Details = "更新全天30分钟曲线，并重新规划补水计划"
		}

	case ScenarioPeriodicCalibration:
		// 定期校准：验证一致性，根据累积误差决定
		cumulativeError := da.calculateCumulativeError()
		decision.ShouldUpdate30min = cumulativeError > 15.0
		decision.ShouldReaggregate3h = cumulativeError > 15.0
		decision.ShouldReplan = cumulativeError > 20.0
		decision.Reason = fmt.Sprintf("定期校准（每3h），累积误差%.1f%%", cumulativeError)

		if cumulativeError > 20.0 {
			decision.UpdateRange30min = "全天"
			decision.Details = "累积误差较大，需要重新规划"
		} else if cumulativeError > 15.0 {
			decision.UpdateRange30min = "6h"
			decision.Details = "累积误差中等，更新近期预测"
		} else {
			decision.Details = "累积误差较小，保持当前计划"
		}

		// 更新校准时间
		da.lastCalibration = currentTime
	}

	return decision
}

// getConsecutiveTrendCount 获取连续趋势的记录数
func (da *DeviationAnalyzer) getConsecutiveTrendCount() int {
	if len(da.history) < 2 {
		return 0
	}

	count := 0
	var lastSign bool
	firstIteration := true

	for i := len(da.history) - 1; i >= 0; i-- {
		record := da.history[i]
		if math.Abs(record.Deviation) <= 10.0 {
			break
		}

		currentSign := record.Deviation > 0
		if firstIteration {
			lastSign = currentSign
			count = 1
			firstIteration = false
		} else if currentSign == lastSign {
			count++
		} else {
			break
		}
	}

	return count
}

// calculateCumulativeError 计算累积误差
func (da *DeviationAnalyzer) calculateCumulativeError() float64 {
	if len(da.history) == 0 {
		return 0
	}

	// 计算最近若干次记录的平均绝对偏差
	sum := 0.0
	count := 0
	maxRecords := 6 // 最近6次记录（3小时）

	start := len(da.history) - maxRecords
	if start < 0 {
		start = 0
	}

	for i := start; i < len(da.history); i++ {
		sum += math.Abs(da.history[i].Deviation)
		count++
	}

	if count == 0 {
		return 0
	}

	return sum / float64(count)
}

// PrintDecision 打印决策信息
func PrintDecision(decision *UpdateDecision) {
	logTitle(LogLevelInfo, "更新决策分析")

	logMessage(LogLevelInfo, "决策时间: %s\n", decision.Timestamp.Format(TimeLayout))
	logMessage(LogLevelInfo, "场景类型: %s\n", decision.Scenario.String())
	logMessage(LogLevelInfo, "决策原因: %s\n", decision.Reason)

	if decision.Details != "" {
		logMessage(LogLevelInfo, "详细信息: %s\n", decision.Details)
	}

	logSeparator(LogLevelInfo, "-", 60)

	// 30分钟曲线更新
	if decision.ShouldUpdate30min {
		logMessage(LogLevelInfo, "✓ 更新30分钟曲线\n")
		logMessage(LogLevelInfo, "  - 更新范围: %s\n", decision.UpdateRange30min)
		logMessage(LogLevelInfo, "  - 更新方式: %s\n", decision.UpdateMethod)
	} else {
		logMessage(LogLevelInfo, "○ 不更新30分钟曲线\n")
	}

	// 3小时曲线重新聚合
	if decision.ShouldReaggregate3h {
		logMessage(LogLevelInfo, "✓ 重新聚合3小时曲线\n")
	} else {
		logMessage(LogLevelInfo, "○ 不重新聚合3小时曲线\n")
	}

	// 补水计划重新规划
	if decision.ShouldReplan {
		logMessage(LogLevelInfo, "✓ 重新规划全天补水计划\n")
	} else {
		logMessage(LogLevelInfo, "○ 不重新规划补水计划\n")
	}

	logSeparator(LogLevelInfo, "=", 60)
}

// GetDeviationHistory 获取偏差历史（用于调试和展示）
func (da *DeviationAnalyzer) GetDeviationHistory() []DeviationRecord {
	return da.history
}

// PrintDeviationHistory 打印偏差历史
func (da *DeviationAnalyzer) PrintDeviationHistory() {
	logTitle(LogLevelInfo, "偏差历史记录")

	if len(da.history) == 0 {
		logMessage(LogLevelInfo, "暂无记录\n")
		return
	}

	logMessage(LogLevelInfo, "%-20s %-12s %-12s %-12s %-10s\n",
		"时间", "实际用量", "预测用量", "偏差", "区间")
	logSeparator(LogLevelInfo, "-", 70)

	for _, record := range da.history {
		sign := "+"
		if record.Deviation < 0 {
			sign = ""
		}
		logMessage(LogLevelInfo, "%-20s %-12.2f %-12.2f %s%-11.1f%% %-10d\n",
			record.Timestamp.Format(TimeLayout),
			record.ActualUsage,
			record.PredictedUsage,
			sign,
			record.Deviation,
			record.BlockIndex)
	}

	logSeparator(LogLevelInfo, "=", 70)
}
